package org.codingq92.basic.io.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * NIO Server端
 *
 * @author: CodingQ92
 * @date: 2025/6/29 22:43
 */
public class NIOServer {
    public static void main(String[] args) {
        try {
            // 1.打开服务端通道
            ServerSocketChannel ssChannel = ServerSocketChannel.open(); // 服务端通道,负责监听客户端连接,类似BIO的ServerSocket
            ssChannel.configureBlocking(false); // 设置为非阻塞模式
            ssChannel.bind(new java.net.InetSocketAddress(18000)); // 绑定端口
            // 2.打开选择器
            Selector selector = Selector.open();
            // 3.将服务端通道注册到选择器，监听【连接事件】
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 4.获取Selector中的已经就绪的事件并根据类型处理
            while (true) {
                // 阻塞等待事件（连接、读、写等），返回就绪的事件数量
                int readyChannels = selector.select();
                if (readyChannels == 0) {
                    System.out.println("没有就绪的事件");
                    continue;
                }
                ;
                // 遍历所有的就绪事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    if (sk.isAcceptable()) { // 处理【连接事件】：有新客户端连接
                        System.out.println("有新客户端连接");
                        handleAccept(ssChannel, selector);
                    } else if (sk.isReadable()) { // 处理【读事件】：客户端有数据发送
                        System.out.println("有客户端数据发送");
                        handleRead(sk);
                    } else if (sk.isWritable()) { // 处理【写事件】：通道可写数据（可选，按需实现）
                        System.out.println("通道可写数据");
                        handleWrite(sk);
                    }
                }
                // 处理完毕移除当前事件
                iterator.remove();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理【读事件】
     *
     * @param sk SelectionKey
     * @throws IOException 读写异常
     */
    private static void handleRead(SelectionKey sk) throws IOException {
        SocketChannel clientChannel = (SocketChannel) sk.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配缓冲区
        int len;
        while ((len = clientChannel.read(buffer)) > 0) {
            buffer.flip(); // 翻转缓冲区
            String message = new String(buffer.array(), 0, len);
            System.out.println("收到客户端消息：" + message);
            buffer.clear(); // 清空缓冲区
        }
    }

    /**
     * 处理读事件
     *
     * @param sk SelectionKey
     * @throws IOException 读写异常
     */
    private static void handleWrite(SelectionKey sk) throws IOException {
        SocketChannel clientChannel = (SocketChannel) sk.channel();
        // 假设已准备好要写的数据
        ByteBuffer buffer = ByteBuffer.wrap("服务端回复".getBytes());
        clientChannel.write(buffer);
        sk.interestOps(SelectionKey.OP_READ); // 写完后重新监听读事件
    }

    /**
     * 处理连接事件
     *
     * @param ssChannel 服务端通道
     * @param selector  选择器
     * @throws IOException IO异常
     */
    private static void handleAccept(ServerSocketChannel ssChannel, Selector selector) throws IOException {
        // 接受客户端连接（非阻塞，无连接时返回 null，但此处已触发 OP_ACCEPT，故一定有连接）
        SocketChannel clientChannel = ssChannel.accept();
        clientChannel.configureBlocking(false); // 客户端通道也设为非阻塞
        // 将客户端通道注册到选择器，监听【读事件】（后续可扩展写事件）
        clientChannel.register(selector, SelectionKey.OP_READ);
    }
}
